﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel.Activation;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.ComponentModel;
using System.Linq.Expressions;
using webjies.Utilities;

namespace webjies.Services
{
    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class JieMapService
    {
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "jies/init")]
        [Description("Init storage")]
        public void Init()
        {
            //TODO Check admin rights
            DataLayer.TableStorage.InitStorage();

            //TODO Check this code:
            // Seems that if you are on development you must have at least one row in each table before using entity group transaction
            // http://blogs.msdn.com/b/blambert/archive/2010/01/06/entity-group-transactions-and-development-storage.aspx
            // Is sufficient add and delete the row
            using (var storage = new DataLayer.TableStorage())
            {
                var jie = new DataLayer.JieEntity() { PartitionKey = "0", RowKey = "0" };
                var node = new DataLayer.JieNodeEntity() { PartitionKey = "0", RowKey = "0" };
                var tag = new DataLayer.JieTagEntity() { PartitionKey = "0", RowKey = "0" };
                var url = new DataLayer.JieUrlEntity() { PartitionKey = "0", RowKey = "0" };
                storage.Add(jie);
                storage.Add(node);
                storage.Add(tag);
                storage.Add(url);
                storage.SaveChanges();
                storage.Delete(jie);
                storage.Delete(node);
                storage.Delete(tag);
                storage.Delete(url);
                storage.SaveChanges();
            }
        }

        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "jies/samples")]
        [Description("Create samples data")]
        public void CreateSamples()
        {
            JieObj jie1 = new JieObj()
                        {
                            Title="Obama",
                            Tags="OBAMA, PRESIDENT, USA, ALL"
                        };
            JieNode jieNode1_1 = new JieNode() { Title = "Wikipedia" };
            jieNode1_1.URLs.Add(new JieUrl() { URL = "http://en.wikipedia.org/wiki/Barack_Obama" });
            jieNode1_1.URLs.Add(new JieUrl() { URL = "http://www.whitehouse.gov/administration/president-obama/" });
            jie1.Nodes.Add(jieNode1_1);

            JieNode jieNode1_2 = new JieNode() { Title = "Homepage" };
            jieNode1_2.URLs.Add(new JieUrl() { URL = "http://www.barackobama.com/" });
            jie1.Nodes.Add(jieNode1_2);

            JieNode jieNode1_3 = new JieNode() { Title = "Facebook" };
            jieNode1_3.URLs.Add(new JieUrl() { URL = "http://www.facebook.com/barackobama" });
            jie1.Nodes.Add(jieNode1_3);

            InsertOrUpdate(jie1);


            JieObj jie2 = new JieObj()
            {
                Title = "Libya War",
                Tags = "WAR, USA, ALL"
            };
            JieNode jieNode2_1 = new JieNode() { Title = "Country" };
            jieNode2_1.URLs.Add(new JieUrl() { URL = "http://en.wikipedia.org/wiki/Libya" });
            jie2.Nodes.Add(jieNode2_1);

            JieNode jieNode2_2 = new JieNode() { Title = "War" };
            jieNode2_2.URLs.Add(new JieUrl() { URL = "http://en.wikipedia.org/wiki/2011_Libyan_civil_war" });
            jie2.Nodes.Add(jieNode2_2);

            InsertOrUpdate(jie2);
        }

        [OperationContract]
        [WebGet(UriTemplate = "jies?s={search}")]
        [Description("Search")]
        public IEnumerable<JieRef> Search(string search)
        {
            using (var storage = new DataLayer.TableStorage())
            {
                IQueryable<DataLayer.JieEntity> query = storage.Query<DataLayer.JieEntity>();

                if (!string.IsNullOrEmpty(search))
                {
                    IQueryable<DataLayer.JieTagEntity> queryTag = storage.Query<DataLayer.JieTagEntity>();

                    var searchParts = search.Split(' ').Select(p => p.ToUpper());

                    Expression<Func<DataLayer.JieTagEntity, bool>> predicate = null;
                    foreach (var part in searchParts)
                    {
                        if (predicate == null)
                            predicate = p => p.RowKey == part;
                        else
                            predicate = ExpressionCombine.OrElse(predicate, p => p.RowKey == part);
                    }
                    queryTag = queryTag.Where(predicate);

                    var jieWithTag = queryTag.ToList();

                    var jieIds = (from t in jieWithTag
                                  group t by t.PartitionKey into gt
                                  where searchParts.Intersect(gt.Select(p => p.RowKey)).Count() == searchParts.Count()
                                  select gt.Key);
                    if (jieIds.Count() > 0)
                    {
                        Expression<Func<DataLayer.JieEntity, bool>> predicateJie = null;
                        foreach (var id in jieIds)
                        {
                            string searchId = id;
                            if (predicateJie == null)
                                predicateJie = p => p.PartitionKey == searchId;
                            else
                                predicateJie = ExpressionCombine.OrElse(predicateJie, p => p.PartitionKey == searchId);
                        }

                        query = query.Where(predicateJie);
                    }
                    else
                        query = null;
                }

                if (query != null)
                    return query.ToList().Select(p => p.ToJieRef());
                else
                    return new JieRef[0];
            }
        }

        [OperationContract]
        [WebGet(UriTemplate = "jies/{id}")]
        [Description("Get")]
        public JieObj Get(string id)
        {
            using (var storage = new DataLayer.TableStorage())
            {
                var query = storage.Query<DataLayer.JieEntity>();

                var jie = query.FinByKey(id);
                if (jie == null)
                    return null;
                else
                {
                    return jie.ToJieObj(storage);
                }
            }
        }

        [OperationContract]
        [WebInvoke(Method = "DELETE", UriTemplate = "jies/{id}")]
        [Description("Delete")]
        public void Delete(string id)
        {
            using (var storage = new DataLayer.TableStorage())
            {
                var query = storage.Query<DataLayer.JieEntity>();

                var jie = query.FinByKey(id);

                if (jie == null)
                    throw new ApplicationException(string.Format("Jie [{0}] not found", id));

                storage.Delete(jie);
                storage.SaveChanges();
            }
        }

        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "jies")]
        [Description("InsertOrUpdate")]
        public JieObj InsertOrUpdate(JieObj item)
        {
            if (item == null)
                throw new ArgumentNullException("item");
            System.Diagnostics.Debug.Assert(string.IsNullOrEmpty(item.ID));

            using (var storage = new DataLayer.TableStorage())
            {
                DataLayer.JieEntity entity = null;
                if (string.IsNullOrEmpty(item.ID))
                    item.ID = Guid.NewGuid().ToString();
                else
                    entity = storage.Query<DataLayer.JieEntity>().FinByKey(item.ID);

                if (entity == null)
                {
                    entity = new DataLayer.JieEntity();
                    item.UpdateJieEntity(entity);
                    storage.Add(entity);
                }
                else
                {
                    item.UpdateJieEntity(entity);
                    storage.Update(entity);
                }

                item.UpdateJieTags(entity, storage);
                item.UpdateJieNodes(entity, storage);

                storage.SaveChangesBatch();

                return item;
            }
        }

        //[OperationContract]
        //[WebInvoke(Method = "POST", UriTemplate = "jies/{id}")]
        //[Description("Update")]
        //public JieObj Update(string id, JieObj item)
        //{
        //    throw new NotImplementedException();
        //}
    }
}